package cn.szv5.register.server;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

/**
 * 应用模块名称<p>
 * 注册表<p>
 * Copyright: Copyright (C) 2022 , Inc. All rights reserved. <p>
 * Company: 阿飞工作室<p>
 *
 * @author Carl
 * @since 2022/2/8 10:42
 */
public class ServiceRegistry {

    // 检查注册表更新检频率
    public static final long RECENTLY_CHANGED_ITEM_CHECK_INTERVAL = 3000L;
    // 注册表多久没更新检查
    public static final long RECENTLY_CHANGED_ITEM_EXPIRED_INTERVAL = 3 * 60 * 1000L;

    /**
     * 注册表是一个单例
     */
    private static ServiceRegistry instance = new ServiceRegistry();

    /**
     * 核心的内存数据结构：注册表
     */
    private Map<String, Map<String, ServiceInstance>> registry = new HashMap<String, Map<String, ServiceInstance>>();

    private LinkedList<RecentlyChangedServiceInstance> recentlyChangedQueue = new LinkedList();

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private ReadLock readLock = lock.readLock();
    private WriteLock writeLock = lock.writeLock();

    /**
     * 服务注册表的锁
     */
    private ServiceRegistry() {
        // 启动后台进程监控最近变更的队列
        RecentlyChangedQueueMonitor recentlyChangedQueueMonitor = new RecentlyChangedQueueMonitor();
        recentlyChangedQueueMonitor.setDaemon(true);
        recentlyChangedQueueMonitor.start();
    }

    /**
     * 加读锁
     */
    public void readLock() {
        this.readLock.lock();
    }

    /**
     * 释放读锁
     */
    public void readUnlock() {
        this.readLock.unlock();
    }

    /**
     * 加写锁
     */
    public void writeLock() {
        this.writeLock.lock();
    }

    /**
     * 释放写锁
     */
    public void writeUnlock() {
        this.writeLock.unlock();
    }

    /**
     * 服务注册
     *
     * @param serviceInstance
     */
    public void register(ServiceInstance serviceInstance) {

        try {
            this.writeLock();

            // 将服务实例放入最近变更的队列中
            RecentlyChangedServiceInstance recentlyChangedItem =
                new RecentlyChangedServiceInstance(serviceInstance, System.currentTimeMillis(),
                    ServiceInstanceOperation.REGISTER);
            recentlyChangedQueue.offer(recentlyChangedItem);
            System.out.println("最近变更队列：" + recentlyChangedQueue);

            // 将服务实例放入注册表中
            Map<String, ServiceInstance> serviceInstanceMap = registry.get(serviceInstance.getServiceName());
            if (serviceInstanceMap == null) {
                serviceInstanceMap = new HashMap<String, ServiceInstance>();
                serviceInstanceMap.put(serviceInstance.getServiceInstanceId(), serviceInstance);
                registry.put(serviceInstance.getServiceName(), serviceInstanceMap);
            }
            System.out.println("注册表：" + registry);
        } finally {
            this.writeUnlock();
        }

    }

    /**
     * 获取服务实例
     *
     * @param serviceName
     * @param serviceInstanceId
     * @return
     */
    public ServiceInstance getServiceInstance(String serviceName, String serviceInstanceId) {

        try {
            this.readLock();
            Map<String, ServiceInstance> serviceInstanceMap = registry.get(serviceName);
            return serviceInstanceMap.get(serviceInstanceId);
        } finally {
            this.readUnlock();
        }
    }

    /**
     * 获取整个注册表
     *
     * @return
     */
    public Map<String, Map<String, ServiceInstance>> getRegistry() {
        return registry;
    }

    /**
     * 获取最近有变化的注册表
     *
     * @return
     */
    public DeltaRegistry getDeltaRegistry() {
        long totalCount = 0L;
        for (Map<String, ServiceInstance> serviceInstanceMap : registry.values()) {
            totalCount += serviceInstanceMap.size();
        }
        DeltaRegistry deltaRegistry = new DeltaRegistry(recentlyChangedQueue, totalCount);
        return deltaRegistry;
    }

    /**
     * 从注册表删除一个服务实例
     *
     * @param serviceName
     * @param serviceInstance
     */
    public void remove(String serviceName, ServiceInstance serviceInstance) {
        try {
            this.writeLock();

            // 将服务实例变更信息放入队列中
            RecentlyChangedServiceInstance recentlyChangedItem =
                new RecentlyChangedServiceInstance(serviceInstance, System.currentTimeMillis(),
                    ServiceInstanceOperation.REMOVE);
            recentlyChangedQueue.offer(recentlyChangedItem);
            System.out.println("最近变更队列：" + recentlyChangedQueue);

            registry.get(serviceName).remove(serviceInstance.getServiceInstanceId());
            System.out.println("服务实例【" + serviceInstance + "】,从注册表中摘除......");
        } finally {
            this.writeUnlock();
        }

    }

    /**
     * 获取服务注册表单例
     *
     * @return
     */
    public static ServiceRegistry getInstance() {
        return instance;
    }

    /**
     * 最近变化的服务实例
     */
    class RecentlyChangedServiceInstance {
        /**
         * 服务实例
         */
        ServiceInstance serviceInstance;
        /**
         * 发生变更的时间戳
         */
        long changedTimestamp;
        /**
         * 变更操作
         */
        String serviceOperation;

        public RecentlyChangedServiceInstance(ServiceInstance serviceInstance, long changedTimestamp,
            String serviceOperation) {
            this.serviceInstance = serviceInstance;
            this.changedTimestamp = changedTimestamp;
            this.serviceOperation = serviceOperation;
        }
    }

    /**
     * 服务实例操作
     */
    class ServiceInstanceOperation {
        /**
         * 注册
         */
        public static final String REGISTER = "register";
        /**
         * 删除
         */
        public static final String REMOVE = "remove";
    }

    /**
     * 最近变更队列的监控线程
     */
    class RecentlyChangedQueueMonitor extends Thread {
        @Override
        public void run() {
            while (true) {
                try {
                    synchronized (instance) {

                        RecentlyChangedServiceInstance recentlyChangedItem = null;
                        long currentTimestamp = System.currentTimeMillis();
                        while ((recentlyChangedItem = recentlyChangedQueue.peek()) != null) {
                            // 判断如果一个服务实例变更信息已经在队列里存在超过3分钟，就从队列里移除
                            if (currentTimestamp - recentlyChangedItem.changedTimestamp
                                > RECENTLY_CHANGED_ITEM_EXPIRED_INTERVAL) {
                                recentlyChangedQueue.pop();
                            }
                        }
                        Thread.sleep(RECENTLY_CHANGED_ITEM_CHECK_INTERVAL);

                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
